﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using SharpMap.Geometries;
using System.Diagnostics;

namespace SharpMap.Control
{
    public enum enumMapOperation
    {
        coArrow,
        coZoomIn,
        coZoomOut,
        coPan
    }

    enum enumTracking
    {
        coTrackNothing,
        coTrackRectangle,
        coTrackLine
    }

    public partial class AxSharpMapControl : UserControl
    {
        /// <summary>
        /// Sharp Map Object
        /// </summary>
        private SharpMap.Map _map = new SharpMap.Map();
        /// <summary>
        /// Map Image Cache
        /// </summary>
        private Image _mapImage = null;
        private Rectangle _image_rect = Rectangle.Empty;
        /// <summary>
        /// flag whether the map need be repainted
        /// </summary>
        private bool _repaint = true;
        
        /// <summary>
        /// Map Control Operation
        /// </summary>
        private enumMapOperation _operation = enumMapOperation.coArrow;
        
        #region Tracking Definition
        private enumTracking _tracking = enumTracking.coTrackNothing;
        private RectangleTracker _tracker = null;
        #endregion

        private System.Drawing.Point _mouse_start = new System.Drawing.Point(0, 0);
        private System.Drawing.Point _mouse_moving_1 = new System.Drawing.Point(0, 0);
        private System.Drawing.Point _mouse_moving_2 = new System.Drawing.Point(0, 0);
        private bool _pan_begin = false;

        public AxSharpMapControl()
        {
            InitializeComponent();
        }

        private void AxSharpMapControl_Load(object sender, EventArgs e)
        {
            _map.Size = this.ClientSize;
        }

        #region MapControl Event
        private void AxSharpMapControl_Paint(object sender, PaintEventArgs e)
        {
            Graphics g = e.Graphics;
            Size client_size = this.ClientSize;

            if (this._mapImage == null || _repaint == true)
            {
                this._mapImage = GetMapImage();
                // Init Image Rect
                _image_rect.X = 0;
                _image_rect.Y = 0;
                _image_rect.Width = client_size.Width;
                _image_rect.Height = client_size.Height;
                // Set Repaint false
                _repaint = false;
                Debug.WriteLine("repaint");
            }
            if (this._mapImage != null)
            {
                Console.WriteLine(_image_rect.X.ToString() + _image_rect.Y.ToString());
                g.DrawImage(this._mapImage, _image_rect);
            }
        }

        private void AxSharpMapControl_Resize(object sender, EventArgs e)
        {

        }

        private void AxSharpMapControl_ClientSizeChanged(object sender, EventArgs e)
        {

        }

        private void AxSharpMapControl_MouseDoubleClick(object sender, MouseEventArgs e)
        {
        }

        private void AxSharpMapControl_MouseDown(object sender, MouseEventArgs e)
        {
            switch(_operation)
            {
                case enumMapOperation.coPan:
                    {
                        BeginPan();
                        _mouse_start.X = e.X;
                        _mouse_start.Y = e.Y;
                        _mouse_moving_1 = _mouse_start;
                    }
                    break;
            }
            switch (_tracking)
            {
                case enumTracking.coTrackRectangle:
                    {
                        _tracker = new RectangleTracker(this);
                        _tracker.MouseDown(e.X, e.Y);
                    }
                    break;
                case enumTracking.coTrackLine:
                    break;;
            }
        }

        private void AxSharpMapControl_MouseMove(object sender, MouseEventArgs e)
        {
            switch (_operation)
            {
                case enumMapOperation.coPan:
                    {
                        _mouse_moving_2.X = e.X;
                        _mouse_moving_2.Y = e.Y;
                        if (_pan_begin)
                        {   
                            // Moving Image Rect
                            _image_rect.Offset((_mouse_moving_2.X - _mouse_moving_1.X), (_mouse_moving_2.Y - _mouse_moving_1.Y));
                            _mouse_moving_1 = _mouse_moving_2;
                            RefreshMap();
                        }
                    }
                    break;
            }
            switch (_tracking)
            {
                case enumTracking.coTrackRectangle:
                    {
                        if (_tracker != null)
                        {
                            _tracker.MouseMoving(e.X, e.Y);
                        }                        
                    }
                    break;
                case enumTracking.coTrackLine:
                    break; ;
            }
        }

        private void AxSharpMapControl_MouseUp(object sender, MouseEventArgs e)
        {
            switch (_operation)
            {
                case enumMapOperation.coPan:
                    if (_pan_begin)
                    {
                        EndPan();
                        // Update Map Bounding Box
                        UpdateMapRect(_mouse_start, new System.Drawing.Point(e.X, e.Y));                        
                        SetRepaint();
                        Invalidate();
                    }
                    //_operation = enumMapOperation.coArrow;
                    break;
            }
            switch (_tracking)
            {
                case enumTracking.coTrackRectangle:
                    {
                        if (_tracker!=null)
                        {
                            _tracker.MouseUp(e.X, e.Y);
                            Rectangle rect = _tracker.Rect;
                            BoundingBox box = RectangleScreenToMap(rect);
                            ZoomToBox(box);
                            SetRepaint();
                            _tracker = null;
                        }
                    }
                    break;
                case enumTracking.coTrackLine:
                    break; ;
            }
        }

        private void AxSharpMapControl_SizeChanged(object sender, EventArgs e)
        {
            
        }

#endregion

        #region Layers
        public bool AddLayer(SharpMap.Layers.Layer layer)
        {
            if (layer == null)
            {
                return false;
            }
            this._map.Layers.Add(layer);
            return true;
        }
        #endregion

        #region MapView
        public void ZoomIn()
        {
            _operation = enumMapOperation.coZoomIn;
            _tracking = enumTracking.coTrackRectangle;
        }

        public void ZoomOut()
        {
            _operation = enumMapOperation.coZoomOut;
            //_tracking = enumTracking.coTrackRectangle;
            Rectangle rect = this.ClientRectangle;
            Rectangle rect_inflate = InflateRect(rect, 1.5f);
            BoundingBox box = RectangleScreenToMap(rect_inflate);
            ZoomToBox(box);
            SetRepaint();
        }

        public void Pan()
        {
            _operation = enumMapOperation.coPan ;
            _tracking = enumTracking.coTrackNothing;
        }

        public void ZoomToExtents()
        {
            if (this._map.Layers.Count > 0)
            {
                this._map.ZoomToExtents();
                SetRepaint();
            }
        }

        public void RefreshMap()
        {
            Invalidate();
        }
        #endregion

        #region Methods
        public void TrackLine()
        {
            Tracker tracker = new Tracker(this);
            
            tracker.Dispose();
        }

        public void TrackRectangle()
        {
            _tracking = enumTracking.coTrackRectangle;
        }
        #endregion

        #region Property
        public enumMapOperation Operation
        {
            set { _operation = value;   }
            get {   return _operation;  }
        }
        #endregion

        #region Map View Inner Functions
        private Image GetMapImage()
        {
            Image mapImage = null;
            if (this._map.Layers.Count > 0)
            {
                mapImage = this._map.GetMap();
            }

            return mapImage;
        }
        private bool CalImagePaintingScreenRect(Image image, ref Rectangle image_screen_rect)
        {
            Size image_size;
            Size client_size;
            double image_scale = 1.0f;
            double client_scale = 1.0f;
            double center_x = 0.0f;
            double center_y = 0.0f;
            double image_upper_left_x = 0.0f;
            double image_upper_left_y = 0.0f;
            double screen_image_width = 0.0f;
            double screen_image_height = 0.0f;
            bool is_horizonal = true;

            image_size = image.Size;
            client_size = this.ClientSize;

            if (image_size.Width < 1 || image_size.Height < 1)
            {
                return false;
            }

            if (client_size.Width < 1 || client_size.Height < 1)
            {
                return false;
            }

            center_x = client_size.Width / 2;
            center_y = client_size.Height / 2;

            image_scale = (double)image_size.Width / (double)image_size.Height;
            client_scale = (double)client_size.Width / (double)client_size.Height;
            // is_horizonal为 true，说明图片被横向拉伸了
            // is_horizonal为 flase，说明图片被纵向拉伸了
            is_horizonal = (image_scale < client_scale);

            if (is_horizonal)
            {
                screen_image_height = client_size.Height;
                screen_image_width = image_scale * screen_image_height;

                image_upper_left_x = center_x - screen_image_width / 2;
                image_upper_left_y = 0;
            }
            else
            {
                screen_image_width = client_size.Width;
                screen_image_height = screen_image_width / image_scale;

                image_upper_left_x = 0;
                image_upper_left_y = center_y - screen_image_height / 2;
            }

            image_screen_rect.X = (int)image_upper_left_x;
            image_screen_rect.Y = (int)image_upper_left_y;
            image_screen_rect.Width = (int)screen_image_width;
            image_screen_rect.Height = (int)screen_image_height;

            return true;
        }

        private void SetRepaint()
        {
            this._repaint = true;
        }
        private void ZoomToBox(BoundingBox box)
        {   
            this._map.ZoomToBox(box);
            RefreshMap();
        }

        private void UpdateMapRect(System.Drawing.Point start, System.Drawing.Point end)
        {
            BoundingBox box = this._map.Envelope;
            SharpMap.Geometries.Point sp = this._map.ImageToWorld(new PointF(start.X, start.Y));
            SharpMap.Geometries.Point ep = this._map.ImageToWorld(new PointF(end.X, end.Y));
            SharpMap.Geometries.Point offset = new SharpMap.Geometries.Point((sp.X - ep.X), (sp.Y - ep.Y));
            box.Offset(offset);
            this._map.ZoomToBox(box);
        }

        #endregion

        #region Operation
        private BoundingBox RectangleScreenToMap(Rectangle rect)
        {
            SharpMap.Geometries.Point up = this._map.ImageToWorld(new PointF(rect.Left, rect.Top));
            SharpMap.Geometries.Point lf = this._map.ImageToWorld(new PointF(rect.Right, rect.Bottom));
            BoundingBox box = new BoundingBox(up, lf);
            return box;
        }

        private Rectangle InflateRect(Rectangle rect, float factor)
        {
            int width_2 = rect.Width / 2;
            int height_2 = rect.Height / 2;
            int center_x = rect.X + width_2;
            int center_y = rect.Y + width_2;
            int width_f_2 = (int)(width_2 * factor);
            int height_f_2 = (int)(height_2 * factor);

            int left = center_x - width_f_2;
            int top = center_y - height_f_2;
            Rectangle rect_f = Rectangle.Empty;
            rect_f.X = left;
            rect_f.Y = top;
            rect_f.Width = width_f_2 * 2;
            rect_f.Height = height_f_2 * 2;

            return rect_f;
        }

        private void BeginPan()
        {
            _pan_begin = true;
        }
        private void EndPan()
        {
            _pan_begin = false;
        }
        #endregion

    }
}
